// Import required utilities and facade
importScripts('utils.js', 'trilium_server_facade.js');

// isDevEnv function is imported from trilium_server_facade.js

// Initialize TriliumServerFacade
let triliumServerFacade;
try {
	triliumServerFacade = new TriliumServerFacade();
	
	// Trigger initial search after a short delay
	setTimeout(() => {
		if (triliumServerFacade) {
			triliumServerFacade.triggerSearchForTrilium();
		}
	}, 1000);
} catch (error) {
	console.error('Failed to initialize TriliumServerFacade:', error);
}

// Basic event listeners
chrome.runtime.onInstalled.addListener(() => {
	if (isDevEnv()) {
		chrome.action.setIcon({
			path: 'icons/32-dev.png',
		});
	}
});

// Create context menus
async function createContextMenus() {
	try {
		// First, remove all existing context menus to avoid duplicates
		await chrome.contextMenus.removeAll();
		
		// Then create new ones
		await chrome.contextMenus.create({
			id: "trilium-save-selection",
			title: "保存选中内容到 Trilium",
			contexts: ["selection"]
		});

		await chrome.contextMenus.create({
			id: "trilium-save-cropped-screenshot",
			title: "截图保存到 Trilium",
			contexts: ["page"]
		});

		await chrome.contextMenus.create({
			id: "trilium-save-whole-screenshot",
			title: "保存整页截图到 Trilium",
			contexts: ["page"]
		});

		await chrome.contextMenus.create({
			id: "trilium-save-page",
			title: "保存整个页面到 Trilium",
			contexts: ["page"]
		});

		await chrome.contextMenus.create({
			id: "trilium-save-link",
			title: "保存链接到 Trilium",
			contexts: ["link"]
		});

		await chrome.contextMenus.create({
			id: "trilium-save-image",
			title: "保存图片到 Trilium",
			contexts: ["image"]
		});
		

	} catch (error) {
		console.error('Failed to create context menus:', error);
	}
}

// Create context menus when service worker starts
createContextMenus();

// Keyboard shortcuts
chrome.commands.onCommand.addListener(async function (command) {
    if (command == "saveSelection") {
        await saveSelection();
    } else if (command == "saveWholePage") {
        await saveWholePage();
    } else if (command == "saveTabs") {
        await saveTabs();
    } else if (command == "saveCroppedScreenshot") {
        const activeTab = await getActiveTab();
        await saveCroppedScreenshot(activeTab.url);
    }
});

// Utility functions
async function getActiveTab() {
	const tabs = await chrome.tabs.query({
		active: true,
		currentWindow: true
	});
	return tabs[0];
}

async function getWindowTabs() {
	const tabs = await chrome.tabs.query({
		currentWindow: true
	});
	return tabs;
}

async function sendMessageToActiveTab(message) {
	const activeTab = await getActiveTab();
	if (!activeTab) {
		throw new Error("No active tab.");
	}
	
	// Check if we can inject content script
	try {
		// Try to send message first
		const response = await chrome.tabs.sendMessage(activeTab.id, message);
		return response;
	}
	catch (e) {
		// If content script is not ready, try to inject it
		if (e.message.includes("Receiving end does not exist")) {
			try {
				// Check if we can inject scripts on this page
				const tab = await chrome.tabs.get(activeTab.id);
				
				if (tab.url.startsWith('chrome://') || tab.url.startsWith('chrome-extension://') || tab.url.startsWith('moz-extension://')) {
					throw new Error("Cannot inject content script on this type of page");
				}
				
				// Inject content script
				await chrome.scripting.executeScript({
					target: { tabId: activeTab.id },
					files: ['lib/browser-polyfill.js', 'utils.js', 'content.js']
				});
				
				// Wait a bit for content script to initialize
				await new Promise(resolve => setTimeout(resolve, 1000));
				
				// Try sending message again
				const retryResponse = await chrome.tabs.sendMessage(activeTab.id, message);
				return retryResponse;
			} catch (injectionError) {
				console.error('Injection failed:', injectionError);
				throw new Error(`Failed to inject content script: ${injectionError.message}`);
			}
		}
		throw e;
	}
}

function toast(message, noteId = null, tabIds = null) {
	sendMessageToActiveTab({
		name: 'toast',
		message: message,
		noteId: noteId,
		tabIds: tabIds
	});
}

// Image processing functions
function cropImage(newArea, dataUrl) {
	return new Promise((resolve, reject) => {
		const img = new Image();
		img.onload = function () {
			const canvas = document.createElement('canvas');
			canvas.width = newArea.width;
			canvas.height = newArea.height;
			const ctx = canvas.getContext('2d');
			ctx.drawImage(img, newArea.x, newArea.y, newArea.width, newArea.height, 0, 0, newArea.width, newArea.height);
			resolve(canvas.toDataURL());
		};
		img.src = dataUrl;
	});
}

async function takeCroppedScreenshot(cropRect) {
	const activeTab = await getActiveTab();
	const zoom = await chrome.tabs.getZoom(activeTab.id) * window.devicePixelRatio;
	const newArea = Object.assign({}, cropRect);
	newArea.x *= zoom;
	newArea.y *= zoom;
	newArea.width *= zoom;
	newArea.height *= zoom;
	const dataUrl = await chrome.tabs.captureVisibleTab(null, { format: 'png' });
	return await cropImage(newArea, dataUrl);
}

async function takeWholeScreenshot() {
	return await chrome.tabs.captureVisibleTab(null, { format: 'png' });
}

function blob2base64(blob) {
	return new Promise(resolve => {
		const reader = new FileReader();
		reader.onloadend = function() {
			resolve(reader.result);
		};
		reader.readAsDataURL(blob);
	});
}

async function fetchImage(url) {
	const resp = await fetch(url);
	const blob = await resp.blob();
	return await blob2base64(blob);
}

async function postProcessImage(image) {
	if (image.src.startsWith("data:image/")) {
		image.dataUrl = image.src;
		image.src = "inline." + image.src.substr(11, 3);
	}
	else {
		try {
			image.dataUrl = await fetchImage(image.src, image);
		}
		catch (e) {
	
		}
	}
}

async function postProcessImages(resp) {
	if (resp.images) {
		for (const image of resp.images) {
			await postProcessImage(image);
		}
	}
}

// Main functionality functions
async function saveSelection() {
	try {
		const payload = await sendMessageToActiveTab({name: 'trilium-save-selection'});
		if (!payload) {
			throw new Error("Failed to get selection content from content script");
		}
		await postProcessImages(payload);
		const resp = await triliumServerFacade.callService('POST', 'clippings', payload);
		if (!resp) {
			return;
		}
		toast("选中内容已保存到 Trilium。", resp.noteId);
	} catch (error) {
		console.error('Error saving selection:', error);
		toast(`Failed to save selection: ${error.message}`);
	}
}

async function getImagePayloadFromSrc(src, pageUrl) {
	const activeTab = await getActiveTab();
	return {
		title: activeTab.title,
		content: `<img src="${src}" />`,
		pageUrl: pageUrl,
		clipType: 'image'
	};
}

async function saveCroppedScreenshot(pageUrl) {
	try {
		const activeTab = await getActiveTab();
		const cropRect = await sendMessageToActiveTab({name: 'trilium-get-crop-rect'});
		if (!cropRect) {
			throw new Error("Failed to get crop rectangle from content script");
		}
		const dataUrl = await takeCroppedScreenshot(cropRect);
		const payload = await getImagePayloadFromSrc(dataUrl, pageUrl);
		const resp = await triliumServerFacade.callService("POST", "clippings", payload);
		if (!resp) {
			return;
		}
		toast("截图已保存到 Trilium。", resp.noteId);
	} catch (error) {
		console.error('Error saving cropped screenshot:', error);
		toast(`Failed to save cropped screenshot: ${error.message}`);
	}
}

async function saveWholeScreenshot(pageUrl) {
	try {
		const activeTab = await getActiveTab();
		const dataUrl = await takeWholeScreenshot();
		const payload = await getImagePayloadFromSrc(dataUrl, pageUrl);
		const resp = await triliumServerFacade.callService("POST", "clippings", payload);
		if (!resp) {
			return;
		}
		toast("整页截图已保存到 Trilium。", resp.noteId);
	} catch (error) {
		console.error('Error saving whole screenshot:', error);
	}
}

async function saveImage(srcUrl, pageUrl) {
	try {
		const payload = await getImagePayloadFromSrc(srcUrl, pageUrl);
		const resp = await triliumServerFacade.callService("POST", "clippings", payload);
		if (!resp) {
			return;
		}
		toast("图片已保存到 Trilium。", resp.noteId);
	} catch (error) {
		console.error('Error saving image:', error);
	}
}

async function saveWholePage() {
	try {
		const payload = await sendMessageToActiveTab({name: 'trilium-save-page'});
		
		if (!payload) {
			throw new Error("Failed to get page content from content script");
		}
		
		await postProcessImages(payload);
		
		const resp = await triliumServerFacade.callService('POST', 'notes', payload);
		if (!resp) {
			return;
		}
		toast("页面已保存到 Trilium。", resp.noteId);
	} catch (error) {
		console.error('Error saving whole page:', error);
		// Show user-friendly error message
		toast(`Failed to save page: ${error.message}`);
	}
}

async function saveLinkWithNote(title, content) {
	try {
		const activeTab = await getActiveTab();
		if (!title.trim()) {
			title = activeTab.title;
		}
		const resp = await triliumServerFacade.callService('POST', 'notes', {
			title: title,
			content: content,
			clipType: 'note',
			pageUrl: activeTab.url
		});
		if (!resp) {
			return false;
		}
		toast("带备注的链接已保存到 Trilium。", resp.noteId);
		return true;
	} catch (error) {
		console.error('Error saving link with note:', error);
		return false;
	}
}

async function getTabsPayload(tabs) {
	let content = '<ul>';
	tabs.forEach(tab => {
		content += `<li><a href="${tab.url}">${tab.title}</a></li>`
	});
	content += '</ul>';

	const domainsCount = tabs.map(tab => tab.url)
		.reduce((acc, url) => {
			const hostname = new URL(url).hostname
			return acc.set(hostname, (acc.get(hostname) || 0) + 1)
		}, new Map());

	let topDomains = [...domainsCount]
		.sort((a, b) => {return b[1]-a[1]})
		.slice(0,3)
		.map(domain=>domain[0])
		.join(', ')

	if (tabs.length > 3) { topDomains += '...' }

	return {
		title: `${tabs.length} browser tabs: ${topDomains}`,
		content: content,
		clipType: 'tabs'
	};
}

async function saveTabs() {
	try {
		const tabs = await getWindowTabs();
		const payload = await getTabsPayload(tabs);
		const resp = await triliumServerFacade.callService('POST', 'notes', payload);
		if (!resp) {
			return;
		}
		const tabIds = tabs.map(tab=>{return tab.id});
		toast(`已保存 ${tabs.length} 个链接到 Trilium。`, resp.noteId, tabIds);
	} catch (error) {
		console.error('Error saving tabs:', error);
	}
}

// Context menu handler
chrome.contextMenus.onClicked.addListener(async function(info, tab) {

	try {
		if (info.menuItemId === 'trilium-save-selection') {
			await saveSelection();
		}
		else if (info.menuItemId === 'trilium-save-cropped-screenshot') {
			await saveCroppedScreenshot(info.pageUrl);
		}
		else if (info.menuItemId === 'trilium-save-whole-screenshot') {
			await saveWholeScreenshot(info.pageUrl);
		}
		else if (info.menuItemId === 'trilium-save-image') {
			await saveImage(info.srcUrl, info.pageUrl);
		}
		else if (info.menuItemId === 'trilium-save-link') {
			const link = document.createElement("a");
			link.href = info.linkUrl;
			link.appendChild(document.createTextNode(info.linkText || info.linkUrl));

			const activeTab = await getActiveTab();
			const resp = await triliumServerFacade.callService('POST', 'clippings', {
				title: activeTab.title,
				content: link.outerHTML,
				pageUrl: info.pageUrl
			});

			if (!resp) {
				return;
			}
			toast("链接已保存到 Trilium。", resp.noteId);
		}
		else if (info.menuItemId === 'trilium-save-page') {
			await saveWholePage();
		}
		else {
			console.error("Unrecognized menuItemId", info.menuItemId);
		}
	} catch (error) {
		console.error('Error handling context menu:', error);
	}
});

// Message handler
chrome.runtime.onMessage.addListener((request, sender, sendResponse) => {
	// Handle async operations properly
	(async () => {
		try {
			if (request.name === 'test') {
				sendResponse({status: 'Service Worker is working!'});
				return;
			}
			else if (request.name === 'openNoteInTrilium') {
				const resp = await triliumServerFacade.callService('POST', 'open/' + request.noteId);
				if (!resp) {
					sendResponse(null);
					return;
				}
				if (resp.result === 'open-in-browser') {
					const {triliumServerUrl} = await chrome.storage.sync.get("triliumServerUrl");
					if (triliumServerUrl) {
						const noteUrl = triliumServerUrl + '/#' + request.noteId;
	
						chrome.tabs.create({
							url: noteUrl
						});
					}
					else {
						console.error("triliumServerUrl not found in local storage.");
					}
				}
				sendResponse(resp);
			}
			else if (request.name === 'closeTabs') {
				const result = await chrome.tabs.remove(request.tabIds);
				sendResponse(result);
			}
			else if (request.name === 'load-script') {
				const result = await chrome.scripting.executeScript({
					target: { tabId: sender.tab.id },
					files: [request.file]
				});
				sendResponse(result);
			}
			else if (request.name === 'save-cropped-screenshot') {
				const activeTab = await getActiveTab();
				const result = await saveCroppedScreenshot(activeTab.url);
				sendResponse(result);
			}
			else if (request.name === 'save-whole-screenshot') {
				const activeTab = await getActiveTab();
				const result = await saveWholeScreenshot(activeTab.url);
				sendResponse(result);
			}
			else if (request.name === 'save-whole-page') {
				const result = await saveWholePage();
				sendResponse(result);
			}
			else if (request.name === 'save-link-with-note') {
				const result = await saveLinkWithNote(request.title, request.content);
				sendResponse(result);
			}
			else if (request.name === 'save-tabs') {
				const result = await saveTabs();
				sendResponse(result);
			}
			else if (request.name === 'trigger-trilium-search') {
				triliumServerFacade.triggerSearchForTrilium();
				sendResponse({status: 'searching'});
			}
					else if (request.name === 'send-trilium-search-status') {
			// If no status yet, trigger a search first
			if (!triliumServerFacade.triliumSearch) {
				await triliumServerFacade.triggerSearchForTrilium();
			}
			
			// Send the status directly in the response
			sendResponse({
				name: "trilium-search-status",
				triliumSearch: triliumServerFacade.triliumSearch
			});
		}
		else if (request.name === 'trilium-search-status') {
			// This is a message sent TO the popup, not FROM the popup
			// We don't need to handle it here, just acknowledge receipt
			sendResponse({status: 'received'});
		}
		else if (request.name === 'trilium-previously-visited') {
			// This is a message sent TO the popup, not FROM the popup
			// We don't need to handle it here, just acknowledge receipt
			sendResponse({status: 'received'});
		}
			else if (request.name === 'trigger-trilium-search-note-url') {
				const activeTab = await getActiveTab();
				if (activeTab && activeTab.url) {
					triliumServerFacade.triggerSearchNoteByUrl(activeTab.url);
					sendResponse({status: 'searching'});
				} else {
					sendResponse({error: 'No active tab found'});
				}
			}
			else {
				sendResponse({error: 'Unknown message type'});
			}
		} catch (error) {
			console.error('Error handling message:', error);
			sendResponse({error: error.message});
		}
	})();
	
	// Return true to indicate we will send a response asynchronously
	return true;
});
